package com.nuoniu.sibanyun.service.erp.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.common.lang.DateUtils;
import com.nuoniu.sibanyun.common.lang.StringUtils;
import com.nuoniu.sibanyun.entity.dto.ErpPeriodDto;
import com.nuoniu.sibanyun.entity.dto.ErpPeriodSaveDto;
import com.nuoniu.sibanyun.entity.erp.ErpAccount;
import com.nuoniu.sibanyun.entity.erp.ErpAccountSettle;
import com.nuoniu.sibanyun.entity.erp.ErpPeriod;
import com.nuoniu.sibanyun.entity.finance.VAccountingAuxiliaryLedger;
import com.nuoniu.sibanyun.entity.finance.VAccountingLedger;
import com.nuoniu.sibanyun.entity.inOut.VInOutRecordMain;
import com.nuoniu.sibanyun.entity.vo.ErpPeriodVo;
import com.nuoniu.sibanyun.mapper.erp.ErpPeriodMapper;
import com.nuoniu.sibanyun.mapper.finance.VAccountingAuxiliaryLedgerMapper;
import com.nuoniu.sibanyun.mapper.finance.VAccountingLedgerMapper;
import com.nuoniu.sibanyun.mapper.po.VPoPurchaseOrderMainMapper;
import com.nuoniu.sibanyun.mapper.po.VPurchaseArrivalMainMapper;
import com.nuoniu.sibanyun.mapper.so.VSaleDeliveryMainMapper;
import com.nuoniu.sibanyun.mapper.so.VSaleOrderMainMapper;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpAccountSettleService;
import com.nuoniu.sibanyun.service.erp.IErpPeriodService;
import com.nuoniu.sibanyun.service.finance.IVAccountingVoucherMainService;
import com.nuoniu.sibanyun.service.inOut.IVInOutRecordMainService;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author baiqiang
 * @since 2020-09-22
 */
@Service
public class ErpPeriodServiceImpl extends ServiceImpl<ErpPeriodMapper, ErpPeriod> implements IErpPeriodService {

    @Autowired
    private IErpAccountService erpAccountService;
    @Autowired
    private IVAccountingVoucherMainService accountingVoucherMainService;
    @Autowired
    private IErpPeriodService periodService;
    @Autowired
    private VPoPurchaseOrderMainMapper purchaseOrderMainMapper;
    @Autowired
    private VPurchaseArrivalMainMapper purchaseArrivalMainMapper;
    @Autowired
    private VSaleOrderMainMapper saleOrderMainMapper;
    @Autowired
    private VSaleDeliveryMainMapper saleDeliveryMainMapper;
    @Autowired
    private IVInOutRecordMainService inOutRecordMainService;
    @Autowired
    private IErpAccountSettleService settleService;
    @Autowired
    private VAccountingAuxiliaryLedgerMapper auxiliaryLedgerMapper;
    @Autowired
    private VAccountingLedgerMapper ledgerMapper;

    @Override
    public List<ErpPeriod> listCurrentCompanyErpPeriod() {
        UserInfo userInfo = UserThreadLocal.get();
        QueryWrapper<ErpPeriod> erpPeriodQueryWrapper = new QueryWrapper<>();
        erpPeriodQueryWrapper.eq("company_id",userInfo.getCompanyId());
//todo zy修改，可以删除***********
        ErpAccount lastErpAccount = erpAccountService.getLastErpAccount();
        Integer accountYear = lastErpAccount.getAccountYear();
        erpPeriodQueryWrapper.eq("current_year",accountYear);
//*****************************
        erpPeriodQueryWrapper.orderByAsc("current_period");
        List<ErpPeriod> list = list(erpPeriodQueryWrapper);
        return list;
    }

    @Override
    public List<ErpPeriod> listCurrentErpPeriodByYear(Integer year) {
        UserInfo userInfo = UserThreadLocal.get();
        QueryWrapper<ErpPeriod> erpPeriodQueryWrapper = new QueryWrapper<>();
        erpPeriodQueryWrapper.eq("company_id",userInfo.getCompanyId());
        erpPeriodQueryWrapper.eq("year",year);
        erpPeriodQueryWrapper.orderByAsc("current_period");
        List<ErpPeriod> list = list(erpPeriodQueryWrapper);
        return list;
    }


    @Override
    public List<ErpPeriodVo> listErpPeriodVo() {
        HashMap<String,ErpPeriodVo> map = new HashMap<>();
        List<ErpPeriodVo> list = new ArrayList<>();
        List<ErpPeriod> erpPeriods = listCurrentCompanyErpPeriod();
        erpPeriods.forEach(f->{
            Integer currentYear = f.getCurrentYear();
            ErpPeriodVo erpPeriodVo = map.get(currentYear.toString());
            if(null == erpPeriodVo){
                erpPeriodVo = new ErpPeriodVo();
                erpPeriodVo.setCurrentYear(currentYear);
                erpPeriodVo.setErpPeriodList(new ArrayList<>());
                map.put(currentYear.toString(),erpPeriodVo);
                list.add(erpPeriodVo);
            }
            erpPeriodVo.getErpPeriodList().add(f);
        });
        return list;
    }

    @Override
    public void deleteLastYear(Integer year) {
        if(null == year)throw new NuoNiuException("删除失败,年份不能为空");
        UserInfo userInfo = UserThreadLocal.get();
        String currentYearStr = DateUtils.getYear();
        Integer currentYear = Integer.valueOf(currentYearStr);
        if(currentYear > year)throw new NuoNiuException("删除失败,今年之前的会计期间都不能删除");
        //获取期间 最大年份
        Integer lastYear = baseMapper.getLastYear(userInfo.getCompanyId());
        if(lastYear > year)throw new NuoNiuException("删除失败,请删除最新一年的会计期间");
        QueryWrapper<ErpPeriod> erpPeriodQueryWrapper = new QueryWrapper<>();
        erpPeriodQueryWrapper.eq("company_id",userInfo.getCompanyId());
        erpPeriodQueryWrapper.eq("current_year",year);
        remove(erpPeriodQueryWrapper);
    }

    @Override
    public ErpPeriod getByDate(LocalDate businessDate, Integer companyId, Integer accountId) {
        return baseMapper.getByDate(businessDate, companyId, accountId);
    }

    @Override
    public void settleAccounts(Integer year, Integer month, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        //ErpPeriod period = periodService.getByPeriod(year, month);
//        if(period.getPuTerminalState().equals(0) || period.getSaTerminalState().equals(0) || period.getInTerminalState().equals(0)) {
//            throw new NuoNiuException("该期间进销存还未结账");
//        }
//
        if(status.equals(1)) {
            Integer count = accountingVoucherMainService.getBookCount(year, month, userInfo.getCompanyId());
            if(count > 0) {
                throw new NuoNiuException("该期间下有未记账凭证");
            }

        }

        baseMapper.settleAccounts(year, month, status, userInfo.getCompanyId());
    }

    //zy新增业务结账
    @Override
    @Transactional
    public void BusinessSettleAccounts(String begin, String end, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        LocalDate begin1 = LocalDate.parse(begin);
        LocalDate end1 = LocalDate.parse(end);
        int year = begin1.getYear();
        int monthValue = begin1.getMonthValue();
        if(status.equals(1)) {
            List<VInOutRecordMain> count = accountingVoucherMainService.getBusinessCount(begin1, end1, userInfo.getCompanyId());
            count.removeAll(Collections.singleton(null));
                if(!CollectionUtils.isEmpty(count) && count.size() > 0) {
                    if(count.get(0).getVoucherCode()==null){
                        String msg = "该期间下有还未审核的期初库存。"  ;
                        throw new NuoNiuException(msg);
                    }
                    List<String> collect = count.stream()
                            .map(v -> v.getVoucherCode())
                            .collect(Collectors.toList());
                    String x = "";
                    for (int i = 0; i < collect.size(); i++) {
                        x += collect.get(i)+"\t";
                    }
                    String msg = "该期间下有还未出入库的单据例如" + x ;
                    throw new NuoNiuException(msg);
                }

        }
        baseMapper.BusinessSettleAccounts(year, monthValue, status, userInfo.getCompanyId());
    }
    @Override
    public ErpPeriod getByPeriod(Integer accountYear, Integer accountPeriod) {
        UserInfo userInfo = UserThreadLocal.get();
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("company_id", userInfo.getCompanyId());
        wrapper.eq("current_year", accountYear);
        wrapper.eq("current_period", accountPeriod);
        wrapper.last("LIMIT 1");
        return getOne(wrapper);
    }

    @Override
    public ErpPeriod getByDate(LocalDate now) {
        UserInfo userInfo = UserThreadLocal.get();
        String format = DateTimeFormatter.ofPattern("yy-MM-dd").format(now);
        ErpPeriod erpPeriod = baseMapper.getByDateStr(userInfo.getCompanyId(), format);
        return erpPeriod;
    }

    @Override
    public ErpPeriod checkErpPeriodByDate(LocalDate now, String checkType) {
        ErpPeriod erpPeriod = this.getByDate(now);
        if (StringUtils.isNotBlank(checkType)){
            String[] arr = StringUtils.split(",");
            for (int i = 0; i <arr.length ; i++) {
                String type = arr[i];
                if (StringUtils.isBlank(type))continue;
                switch (type){
                    case "1": if (1 == erpPeriod.getInTerminalState())throw new NuoNiuException("该期间库存已结存");break;
                    case "2": if (1 == erpPeriod.getFiTerminalState())throw new NuoNiuException("该期间财务已结存");break;
                    case "3": if (1 == erpPeriod.getPuTerminalState())throw new NuoNiuException("该期间采购已结存");break;
                    case "4": if (1 == erpPeriod.getSaTerminalState())throw new NuoNiuException("该期间销售已结存");break;
                    case "0":
                        if (1 == erpPeriod.getInTerminalState())throw new NuoNiuException("该期间库存已结存");
                        if (1 == erpPeriod.getFiTerminalState())throw new NuoNiuException("该期间财务已结存");
                        if (1 == erpPeriod.getPuTerminalState())throw new NuoNiuException("该期间采购已结存");
                        if (1 == erpPeriod.getSaTerminalState())throw new NuoNiuException("该期间销售已结存");
                        break;
                }
            }

        }
        return erpPeriod;
    }

    @Override
    public void saveErpPeriods(ErpPeriodSaveDto erpPeriodSaveDto) {
        UserInfo userInfo = UserThreadLocal.get();
        //1. 获取账套信息
        Integer accountId = erpPeriodSaveDto.getAccountId();
        if(null == accountId)throw new NuoNiuException("保存失败，请选择账套年份");
        ErpAccount erpAccount = erpAccountService.getById(accountId);
        if(!userInfo.getCompanyId().equals(erpAccount.getCompanyId()))throw new NuoNiuException("保存失败，你无权操作该账套会计期间");
        if(erpAccount.getStatus() == 2)throw new NuoNiuException("保存失败，当前账套已经结算/记账，你无法新增/编辑会计期间");
        Integer enableDuration = erpPeriodSaveDto.getEnableDuration();
        if(erpAccount.getIsFirstYear()){
            if(null == erpAccount.getEnableDuration() ){
                if(null == enableDuration){
                    throw new NuoNiuException("第一年账套请选择启用期间");
                }else{
                    //新增 会计期间、账套开始时间 和 账套结束时间
                    erpAccount.setBeginDate(LocalDate.of(erpAccount.getAccountYear(),enableDuration,1));
                    erpAccount.setEndDate(LocalDate.of(erpAccount.getAccountYear(),12,31));
                    erpAccount.setEnableDuration(enableDuration);
                }
            }else {
                if(erpAccount.getStatus() == 1 && null != enableDuration){
                    throw new NuoNiuException("第一年账套已经开始使用，不能修改启用期间");
                }else {
                    erpAccount.setBeginDate(LocalDate.of(erpAccount.getAccountYear(),enableDuration,1));
                    erpAccount.setEnableDuration(enableDuration);
                }
            }

            erpAccountService.updateById(erpAccount);
        }
        //2. 保存 会计期间
        List<ErpPeriodDto> erpPeriodDtos = erpPeriodSaveDto.getErpPeriodDtos();
        List<ErpPeriod> saveOrUpdateList = new ArrayList<>();
        //2.1 获取 租户 所有的会计期间
        List<ErpPeriod> erpPeriods = listCurrentCompanyErpPeriod();
        //2.2 组装数据
        erpPeriodDtos.stream().forEach(erpPeriodDto->{
            Long erpPeriodDtoId = erpPeriodDto.getId();
            boolean isRepeat = false;
            if(null != erpPeriodDtoId){
                Optional<ErpPeriod> first = erpPeriods.stream().filter(f -> f.getId().equals(erpPeriodDtoId)).findFirst();
                if(first.isPresent()){
                    ErpPeriod erpPeriod = first.get();
                    //2.3 判断 是否 可以修改当前期间
                    if(null != erpPeriod.getIsAdjustment() && !erpPeriod.getIsAdjustment())throw new NuoNiuException("保存失败，数据错误");
                    //2.4 判断 期间是否重复
                    isRepeat = erpPeriods.stream().anyMatch(f -> f.getAccountId().equals(erpPeriodDto.getAccountId()) &&  f.getCurrentYear().equals(erpPeriodDto.getCurrentYear()) && f.getCurrentPeriod().equals(erpPeriodDto.getCurrentPeriod()) && !f.getId().equals(erpPeriodDtoId));
                }
            }else{
                //2.4 判断 期间是否重复
                isRepeat = erpPeriods.stream().anyMatch(f -> f.getAccountId().equals(erpPeriodDto.getAccountId()) && f.getCurrentYear().equals(erpPeriodDto.getCurrentYear()) && f.getCurrentPeriod().equals(erpPeriodDto.getCurrentPeriod()));
            }
            if(isRepeat)throw new NuoNiuException("保存失败，会计期间重复");
            ErpPeriod erpPeriod = new ErpPeriod();
            BeanUtils.copyProperties(erpPeriodDto,erpPeriod);
            erpPeriod.setCompanyId(userInfo.getCompanyId());
            erpPeriod.setAccountId(accountId);
            saveOrUpdateList.add(erpPeriod);
        });
        if(saveOrUpdateList.size() > 0){
            saveOrUpdateBatch(saveOrUpdateList);
        }
    }

    @Override
    public void inventorySettleAccounts(Integer year, Integer month, Integer status, Integer companyId) {
        if(status.equals(0)) {
            throw new NuoNiuException("暂不支持反结账");
        }

        if(month != 1) {
            ErpPeriod period = baseMapper.getDetail(year, month - 1, companyId);
            if(!period.getInventoryTerminalState().equals(1)) {
                throw new NuoNiuException("上一期间还未结账");
            }
        }

        //存货结账
        baseMapper.inventorySettleAccounts(year, month, status, companyId);
    }

    @Override
    public void psiSettleAccounts(Integer year, Integer month, Integer status, Integer type) {
        UserInfo userInfo = UserThreadLocal.get();
        ErpAccount account = erpAccountService.getLastErpAccount();
        ErpPeriod period = periodService.getByPeriod(account.getAccountYear(), month);
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("company_id", userInfo.getCompanyId());
        if (null!=period.getBeginDate() && null!=period.getEndDate())
        {wrapper.between("voucher_date", period.getBeginDate(), period.getEndDate());}
//         wrapper.between("voucher_date", period.getBeginDate(), period.getEndDate());
        wrapper.lt("voucher_state", 1);


        if(type.equals(1)) { //采购结账
            if(status.equals(0)) {
                ErpPeriod downPeriod = periodService.getByPeriod(account.getAccountYear(), month + 1);
                if(downPeriod != null && !downPeriod.getPuTerminalState().equals(0)) {
                    throw new NuoNiuException("下一期间还没有反结账");
                }
            } else {
                ErpPeriod upPeriod = periodService.getByPeriod(account.getAccountYear(), month - 1);
                if(upPeriod != null && !upPeriod.getPuTerminalState().equals(1)) {
                    throw new NuoNiuException("上一期间还没有结账");
                }
            }

            //采购订单
            Integer pOrderCount = purchaseOrderMainMapper.selectCount(wrapper);
            if(pOrderCount > 0) {
                throw new NuoNiuException("该期间,采购订单有未审核的的单据,不能结账");
            }

            //采购到货单
            Integer pPurchaseCount = purchaseArrivalMainMapper.selectCount(wrapper);
            if(pPurchaseCount > 0) {
                throw new NuoNiuException("该期间,采购到货单有未审核的单据,不能结账");
            }

            //采购结账
            baseMapper.updatePuTerminalState(year, month, status, userInfo.getCompanyId());
        }

        if(type.equals(2)) { //销售结账
            if(status.equals(0)) {
                ErpPeriod downPeriod = periodService.getByPeriod(account.getAccountYear(), month + 1);
                if(downPeriod != null && !downPeriod.getSaTerminalState().equals(0)) {
                    throw new NuoNiuException("下一期间还没有反结账");
                }
            } else {
                ErpPeriod upPeriod = periodService.getByPeriod(account.getAccountYear(), month - 1);
                if(upPeriod != null && !upPeriod.getSaTerminalState().equals(1)) {
                    throw new NuoNiuException("上一期间还没有结账");
                }
            }

            //销售订单
            Integer sOrderCount = saleOrderMainMapper.selectCount(wrapper);
            if(sOrderCount > 0) {
                throw new NuoNiuException("该期间,销售订单有未审核的单据,不能结账");
            }

            //销售发货单
            Integer sDeliveryCount = saleDeliveryMainMapper.selectCount(wrapper);
            if(sDeliveryCount > 0) {
                throw new NuoNiuException("该期间,销售发货单有未审核的单据,不能结账");
            }

            //销售结账
            baseMapper.updateSaTerminalState(year, month, status, userInfo.getCompanyId());
        }

        if(type.equals(3)) { //库存结账
            if(status.equals(0)) {
                ErpPeriod downPeriod = periodService.getByPeriod(account.getAccountYear(), month + 1);
                if(downPeriod != null && !downPeriod.getInTerminalState().equals(0)) {
                    throw new NuoNiuException("下一期间还没有反结账");
                }
            } else {
                ErpPeriod upPeriod = periodService.getByPeriod(account.getAccountYear(), month - 1);
                if(upPeriod != null && !upPeriod.getInTerminalState().equals(1)) {
                    throw new NuoNiuException("上一期间还没有结账");
                }
            }


            if(period.getPuTerminalState().equals(0)) {
                throw new NuoNiuException("该期间采购还未结账");
            }

            if(period.getSaTerminalState().equals(0)) {
                throw new NuoNiuException("该期间销售还未结账");
            }

            Integer count =  inOutRecordMainService.count(wrapper);
            if(count > 0) {
                throw new NuoNiuException("库存单据有还未审核！");
            }
            baseMapper.updateInTerminalState(year, month, status, userInfo.getCompanyId());
        }
    }

    @Override
    @Transactional
    public void settleYearAccounts() {
        UserInfo userInfo = UserThreadLocal.get();
        ErpAccount account = erpAccountService.getLastErpAccount();
        ErpPeriod erpPeriod = periodService.getByPeriod(account.getAccountYear(), 12);
        if(erpPeriod != null && !erpPeriod.getFiTerminalState().equals(1)) {
            throw new NuoNiuException("结账失败:12月还未结账");
        }

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("company_id", userInfo.getCompanyId());
        wrapper.eq("account_year", account.getAccountYear());
        ErpAccountSettle accountSettle = settleService.getOne(wrapper);
        if(accountSettle != null) {
            throw new NuoNiuException("已年度结账");
        }

        //结账
        accountSettle = new ErpAccountSettle();
        accountSettle.setAccountYear(account.getAccountYear());
        accountSettle.setCompanyId(userInfo.getCompanyId());
        accountSettle.setCreateUserId(userInfo.getUserId());
        settleService.save(accountSettle);

        //初始化新账套
        account.setAccountYear(account.getAccountYear() + 1);
        account.setEnableDuration(1);
        account.setIsFirstYear(false);
        account.setStatus(1);
        account.setBeginDate(LocalDate.parse(account.getAccountYear() + "-01-01"));
        account.setEndDate(LocalDate.parse(account.getAccountYear() + "-12-31"));
        erpAccountService.updateById(account);
//        ErpAccount erpAccount = new ErpAccount();
//        BeanUtils.copyProperties(account,erpAccount);
//        erpAccount.setAccountYear(account.getAccountYear() + 1);
//        erpAccount.setEnableDuration(1);
//        erpAccount.setIsFirstYear(false);
//        erpAccount.setStatus(1);
//        erpAccount.setBeginDate(LocalDate.parse(account.getAccountYear() + "-01-01"));
//        erpAccount.setEndDate(LocalDate.parse(account.getAccountYear() + "-12-31"));
//        erpAccountService.save(erpAccount);

        //初始化新期间
        List<ErpPeriod> periodList = new ArrayList<>();
        LocalDate date = LocalDate.of(account.getAccountYear(), 12, 01);
        for(int i=12; i>0; i--) {
            LocalDate lastMonth = date.minusMonths(12 - i); // 当前月份减1
            LocalDate firstDay = lastMonth.with(TemporalAdjusters.firstDayOfMonth()); // 获取当前月的第一天
            LocalDate lastDay = lastMonth.with(TemporalAdjusters.lastDayOfMonth()); // 获取当前月的最后一天
            ErpPeriod period = new ErpPeriod();
            period.setAccountId(account.getId().intValue());
            period.setCompanyId(userInfo.getCompanyId());
            period.setBeginDate(firstDay);
            period.setEndDate(lastDay);
            period.setCurrentYear(account.getAccountYear());
            period.setCurrentPeriod(i);
            periodList.add(period);
        }
        periodService.saveBatch(periodList);

        //结转科目余额
        List<VAccountingLedger> ledgerList = ledgerMapper.getYearLedger(account.getAccountYear() - 1, 12, userInfo.getCompanyId());
        for(VAccountingLedger ledger : ledgerList) {
            ledger.setAccountYear(account.getAccountYear());
            ledger.setAccountPeriod(1);
            ledger.setBeginAmountDirection(ledger.getEndAmountDirection());
            ledger.setBeginAmount(ledger.getEndAmount());
            ledger.setEndAmount(new BigDecimal(0.0));
            ledger.setEndAmountDirection(3);
            ledger.setCreditAmount(new BigDecimal(0.0));
            ledger.setDebitAmount(new BigDecimal(0.0));
            ledgerMapper.insert(ledger);
        }

        VAccountingAuxiliaryLedger auxiliaryLedger;

    }


}
